﻿Option Explicit On
'*******************************************************************
' Program: pkgVisio
' Author: Albert E Edlund
' Date:
'
' Purpose:
' Working with office object XML
'
'*******************************************************************


#Region " imports "

Imports System
Imports System.Object
Imports System.IO
Imports System.IO.TextReader
' need to reference windowsbase in .net
Imports System.IO.Packaging

' need for dbconnectionstringbuilder used in clsConnections
Imports System.Data.Common

Imports System.Windows
Imports System.Windows.Forms
Imports System.Text.StringBuilder

Imports Microsoft.VisualBasic

Imports System.Text
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel

Imports System.Xml
Imports System.Linq
Imports System.Linq.Enumerable
Imports System.Windows.Input

Imports System.Xml.Serialization
Imports System.Xml.Linq
Imports System.Xml.XPath

Imports ADODB
Imports System.Data
Imports System.Data.OleDb
Imports System.Data.Sql


Imports Microsoft.Practices.EnterpriseLibrary.Common
Imports Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
Imports Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging

' visio v15 does not use a xName prefix which may put it into conflict with other open office apps
Imports <xmlns="http://schemas.microsoft.com/office/visio/2012/main">
Imports <xmlns:rs="urn:schemas-microsoft-com:rowset">

' generic namespace for office open xml, used in all of them
Imports <xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">

Imports pkgVisio.clsVisPackage
Imports pkgVisio.clsStructures


#End Region


Public Class clsTopology


#Region " Structures "




    '
    ' a begin connection point to an end connection point
    '
    Public Structure stcEdge
        Dim edgeBegin As stcShapeConnect        ' begin connection
        Dim edgeEnd As stcShapeConnect          ' end connection
        Dim edgeDirection As stcFlagEdgeAttr    ' edge flags
        Dim edgeShape As stcPageShape           ' edge shape (if complex flag)
    End Structure

    '
    ' a connection from a page/shape/cell 
    '
    Public Structure stcShapeConnect
        Dim scPageId As Long
        Dim scShapeId As Long
        Dim scConnId As String
        Dim scPart As String

        Public Sub New(ByVal lngPageId As Long, _
                       ByVal lngShapeId As Long, _
                       ByVal strConnId As String, _
                       ByVal shtPart As String)

            scPageId = lngPageId
            scShapeId = lngShapeId
            scConnId = strConnId
            scPart = shtPart
        End Sub

    End Structure


    '
    ' drawing topology edge attributes. In this set of flags we want to attempt to capture
    ' some of the key attributes that will let us know about the edge.
    '   Directionality (single or roundtrip)
    '   complex (is the edge a shape unto itself) and a shape pointer is stored in the stcEdge
    '
    Public Structure stcFlagEdgeAttr

        '
        ' an edge is between two nodes which within visio has directionality
        '
        <FlagsAttribute()>
        Public Enum Flag As ULong
            edgeUndefined = &H0
            edgeSimplex = &H1 '   has a direction component
            edgeHalfDuplex = &H2 ' path can swap directions
            edgeDuplex = &H4  ' path is bi-directional based upon some parameter
            edgeComplex = &H8 ' separate shape (attributes are external)
        End Enum

        '
        '
        '
        Private _ulngMask As ULong
        Public Property ulngMask() As ULong
            Get
                Return _ulngMask
            End Get
            Set(value As ULong)
                _ulngMask = value
            End Set
        End Property

        '
        '
        '
        Private Sub SetField(lclFlag As Flag)
            ulngMask = CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetOn(lclFlag As Flag)
            ulngMask = ulngMask Or CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetOff(lclFlag As Flag)
            ulngMask = ulngMask And Not CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetToggle(lclFlag As Flag)
            ulngMask = ulngMask Xor CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Function AnyOn(lclFlag As Flag) As Boolean
            Return (ulngMask And CULng(lclFlag)) <> 0
        End Function

        '
        '
        '
        Public Function AllOn(lclFlag As Flag) As Boolean
            Return (ulngMask And CULng(lclFlag)) = CULng(lclFlag)
        End Function

        '
        '
        '
        Public Function IsEqual(lclFlag As Flag) As Boolean
            Return ulngMask = CULng(lclFlag)
        End Function

        '
        '
        '
        Public Shared Function DecimalToFlag(dec As Decimal) As Flag
            Dim lclFlag As Flag = Flag.edgeUndefined     ' &H0
            Dim tMsk As ULong = 0
            Dim shift As Byte
            Try
                shift = CByte(Math.Truncate(dec))
                If shift > 0 AndAlso shift <= 64 Then
                    tMsk = CULng(&H1) << (shift - 1)
                End If
                lclFlag = DirectCast(tMsk, Flag)
            Catch e As OverflowException
                'Byte cast operation
                Console.WriteLine("Exception caught in ToStringBin: {0}", e.ToString())
            End Try

            Return lclFlag
        End Function

        '
        '
        '
        Public Function ToStringBin() As [String]
            Dim sRet As [String] = ""
            Try
                sRet = System.Convert.ToString(CLng(ulngMask), 2)
            Catch ex As Exception
                Debug.Print("Exception caught in ToStringBin: {0}", ex.ToString())
            End Try
            Return sRet
        End Function



    End Structure


    '
    ' drawing topology shape attributes
    '
    Public Structure stcFlagShapeAttr


        <FlagsAttribute()>
        Public Enum Flag As ULong
            typeInval = &H0         ' clear flags
            typeShape = &H1         '
            typeGroup = &H2         ' 
            type1D = &H4            ' objtype 2
            typeConnector = &H8     ' use for connector (dynamic, pipe, etc)
        End Enum

        '
        '
        '
        Private _ulngMask As ULong
        Public Property ulngMask() As ULong
            Get
                Return _ulngMask
            End Get
            Set(value As ULong)
                _ulngMask = value
            End Set
        End Property

        '
        '
        '
        Private Sub SetField(lclFlag As Flag)
            ulngMask = CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetOn(lclFlag As Flag)
            ulngMask = ulngMask Or CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetOff(lclFlag As Flag)
            ulngMask = ulngMask And Not CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Sub SetToggle(lclFlag As Flag)
            ulngMask = ulngMask Xor CULng(lclFlag)
        End Sub

        '
        '
        '
        Public Function AnyOn(lclFlag As Flag) As Boolean
            Return (ulngMask And CULng(lclFlag)) <> 0
        End Function

        '
        '
        '
        Public Function AllOn(lclFlag As Flag) As Boolean
            Return (ulngMask And CULng(lclFlag)) = CULng(lclFlag)
        End Function

        '
        '
        '
        Public Function IsEqual(lclFlag As Flag) As Boolean
            Return ulngMask = CULng(lclFlag)
        End Function

        '
        '
        '
        Public Shared Function DecimalToFlag(dec As Decimal) As Flag
            Dim lclFlag As Flag = Flag.typeInval    ' &H0
            Dim tMsk As ULong = 0
            Dim shift As Byte
            Try
                shift = CByte(Math.Truncate(dec))
                If shift > 0 AndAlso shift <= 64 Then
                    tMsk = CULng(&H1) << (shift - 1)
                End If
                lclFlag = DirectCast(tMsk, Flag)
            Catch e As OverflowException
                'Byte cast operation
                Console.WriteLine("Exception caught in ToStringBin: {0}", e.ToString())
            End Try

            Return lclFlag
        End Function

        '
        '
        '
        Public Function ToStringBin() As [String]
            Dim sRet As [String] = ""
            Try
                sRet = System.Convert.ToString(CLng(ulngMask), 2)
            Catch ex As Exception
                Debug.Print("Exception caught in ToStringBin: {0}", ex.ToString())
            End Try
            Return sRet
        End Function


    End Structure


#End Region

#Region " Properties and Declarations "

    '
    '
    '
    Public m_visPackage As clsVisPackage
    Public Property visPackage As clsVisPackage
        Get
            visPackage = Me.m_visPackage
        End Get
        Set(value As clsVisPackage)
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "visPackage is Null.")
            End If
            Me.m_visPackage = value
        End Set
    End Property

    '
    ' 
    '
    Public _xDoc As XDocument
    Public Property xDoc As XDocument
        Get
            Return Me._xDoc
        End Get
        Set(value As XDocument)
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "xDoc is Null.")
            End If
            Me._xDoc = value
        End Set
    End Property

    '
    '
    '
    Private m_visMasters As clsMasters
    Public Property visMasters As clsMasters
        Get
            visMasters = Me.m_visMasters
        End Get
        Set(value As clsMasters)
            Me.m_visMasters = value
        End Set
    End Property


    '
    ' what masters are in the document
    '
    Private _dictMaster As Dictionary(Of Long, String)
    Public Property dictMastersID As Dictionary(Of Long, String)
        Get
            Return Me._dictMaster
        End Get
        Set(value As Dictionary(Of Long, String))
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "dictMastersID is Null.")
            End If
            Me._dictMaster = value
        End Set
    End Property

    '
    ' what shapes are on the page
    '
    Private _dictShapeTypes As Dictionary(Of Long, ULong)
    Public Property dictShapeTypes As Dictionary(Of Long, ULong)
        Get
            Return Me._dictShapeTypes
        End Get
        Set(value As Dictionary(Of Long, ULong))
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "dictShapeTypes is Null.")
            End If
            Me._dictShapeTypes = value
        End Set
    End Property


    '
    '
    '
    Private _lngPgId As Long
    Public Property lngPgId As Long
        Get
            lngPgId = Me._lngPgId
        End Get
        Set(value As Long)
            Me._lngPgId = value
        End Set
    End Property

    '
    '
    '
    Private _bln1Dgroups As Boolean
    Public Property bln1Dgroups As Boolean
        Get
            bln1Dgroups = Me._bln1Dgroups
        End Get
        Set(value As Boolean)
            Me._bln1Dgroups = value
        End Set
    End Property




#End Region



#Region " topology definitions "


    '
    ' since the intent is to not use visio source as part of our project we
    ' have to include some of the enumerations here
    '
    Private Enum FromPart
        visConnectFromError = -1
        visFromNone = 0
        visLeftEdge = 1
        visCenterEdge = 2
        visRightEdge = 3
        visBottomEdge = 4
        visMiddleEdge = 5
        visTopEdge = 6
        visBeginX = 7
        visBeginY = 8
        visBegin = 9
        visEndX = 10
        visEndY = 11
        visEnd = 12
        visFromAngle = 13
        visFromPin = 14
        visControlPoint = 100
    End Enum

    '
    '
    '
    Private Enum toPart
        visConnectToError = -1
        visToNone = 0
        visGuideX = 1
        visGuideY = 2
        visWholeShape = 3
        visGuideIntersect = 4
        visToAngle = 7
        visConnectionPoint = 100
    End Enum

    '
    ' the edge index is the shapeid of the connector on the page
    '
    Private _dictEdge As Dictionary(Of Long, stcEdge)
    Public Property dictEdge As Dictionary(Of Long, stcEdge)
        Get
            Return Me._dictEdge
        End Get
        Set(value As Dictionary(Of Long, stcEdge))
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "dictEdge is Null.")
            End If
            Me._dictEdge = value
        End Set
    End Property

    '
    '
    '
    Private _colPoint As Collection(Of stcShapeConnect)
    Public Property colPoint As Collection(Of stcShapeConnect)
        Get
            colPoint = Me._colPoint
        End Get
        Set(value As Collection(Of stcShapeConnect))
            If (value Is Nothing) Then
                Throw New System.ArgumentNullException("Value", _
                    "colPpoint is Null.")
            End If
            Me._colPoint = value
        End Set
    End Property




#End Region




#Region " Methods "

    '
    ' pass in a page and get a list of connected nodes and edges
    '
    Public Sub loadShapeTopology _
        (ByRef lvControl As ListView)


        Try

            ' setup our list view control
            lvControl.Items.Clear()
            lvControl.Columns.Clear()
            lvControl.View = View.Details
            lvControl.AllowColumnReorder = True
            lvControl.FullRowSelect = True
            lvControl.Columns.Add("Index", 50, HorizontalAlignment.Right)
            lvControl.Columns.Add("BeginNode", 50, HorizontalAlignment.Right)
            lvControl.Columns.Add("BeginName", 150, HorizontalAlignment.Left)
            lvControl.Columns.Add("ConnShp", 50, HorizontalAlignment.Right)
            lvControl.Columns.Add("ConnName", 100, HorizontalAlignment.Left)
            lvControl.Columns.Add("EndNode", 50, HorizontalAlignment.Right)
            lvControl.Columns.Add("EndName", 150, HorizontalAlignment.Left)


            Dim lvItem As ListViewItem
            Dim stcConn As stcPageShape = New stcPageShape
            ' now that the dictionary is complete go add the data to the
            Dim stcConnFlag As stcFlagEdgeAttr = New stcFlagEdgeAttr

            ' listview control
            Dim intIndex As Integer = 0
            For Each edge In dictEdge
                ' add it to the listview control
                intIndex = edge.Key
                lvItem = New ListViewItem(intIndex)
                stcConnFlag = edge.Value.edgeDirection

                ' add the begin shape id
                lvItem.SubItems.Add(edge.Value.edgeBegin.scShapeId.ToString)
                ' add the begin shape nameu
                Dim blnFound As Boolean = True
                Dim pageShape As stcPageShape = New stcPageShape
                With pageShape
                    .psPage = edge.Value.edgeBegin.scPageId
                    .psShape = edge.Value.edgeBegin.scShapeId
                End With
                Try
                    blnFound = Me.visPackage.dictPageShape.ContainsKey(pageShape)
                Catch err As System.Collections.Generic.KeyNotFoundException
                End Try
                If blnFound = True Then
                    lvItem.SubItems.Add(Me.visPackage.dictPageShape(pageShape).strShpNameU.ToString)
                Else
                    lvItem.SubItems.Add(pageShape.psPage.ToString & "_" & pageShape.psShape.ToString)
                End If

                ' add the connector id
                If stcConnFlag.AllOn(stcFlagShapeAttr.Flag.typeConnector) Then
                    With stcConn
                        .psPage = CLng(pageShape.psPage)
                        .psShape = edge.Value.edgeShape.psShape
                    End With
                    lvItem.SubItems.Add(CStr(edge.Value.edgeShape.psShape))

                    ' add the connector nameu
                    blnFound = False
                    Try
                        blnFound = Me.visPackage.dictPageShape.ContainsKey(stcConn)
                    Catch err As System.Collections.Generic.KeyNotFoundException
                    End Try
                    If blnFound = True Then
                        lvItem.SubItems.Add(Me.visPackage.dictPageShape(stcConn).strShpNameU.ToString)
                    Else
                        lvItem.SubItems.Add(stcConn.psPage.ToString & "_" & stcConn.psShape.ToString)
                    End If
                Else
                    lvItem.SubItems.Add("- - -")
                    lvItem.SubItems.Add("- - -")
                End If ' test for dynamic connector


                ' add the end node name

                blnFound = True
                With pageShape
                    .psPage = edge.Value.edgeEnd.scPageId
                    .psShape = edge.Value.edgeEnd.scShapeId
                End With
                lvItem.SubItems.Add(pageShape.psShape.ToString)
                Try
                    blnFound = Me.visPackage.dictPageShape.ContainsKey(pageShape)
                Catch err As System.Collections.Generic.KeyNotFoundException
                End Try
                If blnFound = True Then
                    lvItem.SubItems.Add(Me.visPackage.dictPageShape(pageShape).strShpNameU.ToString)
                Else
                    lvItem.SubItems.Add(pageShape.psPage.ToString & "_" & pageShape.psShape.ToString)
                End If

                ' add it to the listview on the data page
                lvControl.Items.Add(lvItem)

            Next edge

        Catch errXml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(errXml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub

    '
    ' as part of the building of the master dictionary, we include the object type of the master shape
    ' to allow for a test of visLOFlagsRoutable
    '
    Public Sub initMasterData()

        Try


            ' go find the masters.xml file and load it
            Me.visMasters.loadMastersXDoc(Me.visPackage)
            ' go find the individual shape files and load (mirror to a dictionary)
            Me.visMasters.loadMasters(Nothing)

        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub

    '
    ' go through the shapes on the page and attempt to identify object type by first
    ' examining the master shapes associated with them
    '
    Public Sub initShapeTypes()

        Me._dictShapeTypes = Nothing
        Me._dictShapeTypes = New Dictionary(Of Long, ULong)

        Dim lngShapeId As Long

        Try

            ' this is our working shape definition
            Dim newstcShapeType As stcFlagShapeAttr = New stcFlagShapeAttr
            ' this is the visio object type definition
            Dim tstObjType As stcFlagObjType = New stcFlagObjType

            ' get a list of all of the shapes
            Dim qryShapes = Me.xDoc...<Shape>
            Dim intShapes As Integer = qryShapes.Count

            For Each shape In qryShapes

                newstcShapeType.ulngMask = stcFlagShapeAttr.Flag.typeInval
                Dim lngMaster As Long = -1
                Dim bln1D As Boolean = False
                lngShapeId = -1

                Dim tempDoc As XDocument = Nothing
                Using stringReader As New StringReader(shape.ToString)
                    tempDoc = XDocument.Load(XmlReader.Create(stringReader))
                    Me.visPackage.readAttAndElem(tempDoc, "", "Shape")

                    ' look for att=id, att=type, att=master, elem=cell_ObjType
                    If Me.visPackage.attDict.ContainsKey("ID") Then
                        lngShapeId = CLng(Me.visPackage.attDict("ID").ToString)
                    End If

                    ' get the master information first
                    If Me.visPackage.attDict.ContainsKey("Master") = True Then
                        lngMaster = Me.visPackage.attDict("Master")
                        ' go check the master for the object type
                        If -1 < lngMaster Then
                            tstObjType.ulngMask = Me.visMasters.dictMastersID(lngMaster).mstShpObjType.ulngMask
                            bln1D = tstObjType.AnyOn(stcFlagObjType.Flag.visLOFlagsRoutable)
                            If bln1D = True Then
                                newstcShapeType.SetOn(stcFlagShapeAttr.Flag.type1D)
                                Dim strMasterNameU As String = LCase(Me.visMasters.dictMastersID(lngMaster).mstNameU.ToString)

                                ' test for dynamic connector or pipeling (pfd drawings)
                                If (strMasterNameU.Contains("dynamic connector") = True) _
                                    Or (strMasterNameU.Contains("pipeline") = True) Then
                                    newstcShapeType.SetOn(stcFlagShapeAttr.Flag.typeConnector)
                                End If
                            End If ' test for bln1D
                        End If ' test for master key
                    End If ' test for master 

                    If Me.visPackage.attDict.ContainsKey("Type") = True Then
                        If LCase(Me.visPackage.attDict("Type")) = "shape" Then
                            newstcShapeType.SetOn(stcFlagShapeAttr.Flag.typeShape)
                        ElseIf LCase(Me.visPackage.attDict("Type")) = "group" Then
                            newstcShapeType.SetOn(stcFlagShapeAttr.Flag.typeGroup)
                        End If
                    End If

                    ' check for object type to find out if 1D
                    'If Me.visPackage.elemDict.ContainsKey("ObjType") = True Then
                    'End If ' test for objtype element

                End Using

                If Me.dictShapeTypes.ContainsKey(lngShapeId) = False Then
                    Me.dictShapeTypes.Add(lngShapeId, newstcShapeType.ulngMask)
                    'Debug.Print(newstcShapeType.ToStringBin.PadLeft(6, "0"c))
                End If

            Next shape


        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub

    '
    ' get a list of the connection/control points in the connections
    '
    Private Sub buildColOfConnectionPoints()

        Dim intConnection As Integer = 0
        Dim strFromSheet As String = "-1"
        Dim lngFromSheet As Long = -1
        Dim strFromCell As String = ""
        Dim strFromPart As String = ""
        Dim intFromPart As Integer = -1
        Dim strToSheet As String = "-1"
        Dim lngToSheet As Long = -1
        Dim strToCell As String = ""
        Dim strToPart As String = ""
        Dim intToPart As Integer = -1


        Me._colPoint = Nothing
        Me.colPoint = New Collection(Of stcShapeConnect)

        Try

            Dim nodeBegin As stcShapeConnect = New stcShapeConnect
            With nodeBegin
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With
            Dim nodeEnd As stcShapeConnect = New stcShapeConnect
            With nodeEnd
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With


            Dim qryConnections = Me.xDoc...<Connect>
            Dim intConnections As Integer = qryConnections.Count

            For Each connection In qryConnections

                Dim listOfAttributes As IEnumerable(Of XAttribute) = _
                    From att In connection.Attributes() _
                    Select att
                For Each att As XAttribute In listOfAttributes
                    ' Console.WriteLine("{0}={1}", att.Name.LocalName, att.Value)
                    Select Case LCase(att.Name.LocalName)
                        ' from sheet is the dynamic connector
                        Case "fromsheet"
                            'strFromSheet = CStr(att.Value)
                            nodeBegin.scShapeId = CLng(att.Value)
                        Case "fromcell"
                            nodeBegin.scConnId = CStr(att.Value)
                            ' above 100 is a control point
                        Case "frompart"
                            strFromPart = CStr(att.Value)
                            intFromPart = CShort(att.Value)
                            If intFromPart < FromPart.visControlPoint Then
                                strFromPart = CType(intFromPart, FromPart).ToString
                            Else
                                strFromPart = "visCtrlPt" & CStr(intFromPart - FromPart.visControlPoint + 1)
                            End If
                            nodeBegin.scPart = strFromPart

                        Case "tosheet"
                            'strToSheet = CStr(att.Value)
                            nodeEnd.scShapeId = CLng(att.Value)
                        Case "tocell"
                            nodeEnd.scConnId = CStr(att.Value)
                            ' above 100 is connection point
                        Case "topart"
                            intToPart = CShort(att.Value)
                            strToPart = CStr(att.Value)
                            If intToPart < toPart.visConnectionPoint Then
                                strToPart = CType(intToPart, toPart).ToString
                            Else
                                strToPart = "visConnPt" & CStr(intToPart - toPart.visConnectionPoint + 1)
                            End If
                            nodeEnd.scPart = strToPart
                    End Select
                Next att

                If colPoint.Contains(nodeBegin) = False Then
                    colPoint.Add(nodeBegin)
                End If
                If colPoint.Contains(nodeEnd) = False Then
                    colPoint.Add(nodeEnd)
                End If

            Next connection


        Catch errXml As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(errXml, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

        ' Debug.Print(colPoint.Count)

    End Sub

    '
    ' compare the connections point collection with the shapes and remove shapes that are not
    ' in the connection point data (get rid of orphan shapes)
    '
    Private Sub clearUnconnectedShapes()

        Try

            Dim blnShapeFound As Boolean = False


            Dim lngX As Long
            Dim lngShapeId As Long
            Dim dictKey As Dictionary(Of Long, ULong).KeyCollection
            dictKey = dictShapeTypes.Keys
            Dim intCt As Integer = dictKey.Count
            For lngX = dictKey.Count To 0 Step -1
                lngShapeId = dictKey(lngX)
                blnShapeFound = False
                For Each conn In colPoint
                    Dim lngConnShpId As Long = conn.scShapeId
                    If lngConnShpId = lngShapeId Then
                        blnShapeFound = True
                        Exit For
                    End If
                Next conn
                If blnShapeFound = False Then
                    dictShapeTypes.Remove(lngShapeId)
                End If
            Next lngX

            ' Debug.Print(CStr(dictShapeTypes.Count))

        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub


    '
    '
    '
    Private Sub processConnections()

        Try

            Me.dictEdge.Clear()

            ' build our flag attribute structure
            Dim stcConn As stcFlagEdgeAttr = New stcFlagEdgeAttr
            ' build our flag shape structure
            Dim stcShape As stcFlagShapeAttr = New stcFlagShapeAttr

            '
            ' go look for connections with dynamic connectors first
            '
            For Each conn In colPoint
                Dim lngConnShapeId As Long = conn.scShapeId
                stcShape.ulngMask = dictShapeTypes(lngConnShapeId)
                If stcShape.AllOn(stcFlagShapeAttr.Flag.typeConnector) Then
                    prebuildConnectorEdges(conn)
                End If ' test for dynamic connector 
            Next conn

            Dim intCt As Integer = dictEdge.Count

            Dim qryConnections = Me.xDoc...<Connect>
            Dim intConnections As Integer = qryConnections.Count

            For Each link In qryConnections

                processConnectionXelement(link)

            Next link



        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try

    End Sub


    '
    ' pass in the shape connection that has to be processed
    ' we check to see if a "complex" edge has been created and if not add
    ' a new edge to the dictEdges.
    '
    Private Sub prebuildConnectorEdges(ByVal colPoint As stcShapeConnect)

        Try


            ' build our flag attribute structure
            Dim stcConn As stcFlagEdgeAttr = New stcFlagEdgeAttr
            stcConn.ulngMask = stcFlagEdgeAttr.Flag.edgeUndefined
            ' by definition a dynamic connector is simplex (directional shape), can be changed later
            stcConn.SetOn(stcFlagEdgeAttr.Flag.edgeSimplex)
            ' the edge will use an external shape
            stcConn.SetOn(stcFlagEdgeAttr.Flag.edgeComplex)

            ' build the page shape structure
            Dim stcNewEdge As stcEdge = New stcEdge
            Dim newPS As stcPageShape = New stcPageShape
            With newPS
                .psPage = colPoint.scPageId
                .psShape = colPoint.scShapeId
            End With

            ' build the two end nodes
            Dim nodeBegin As stcShapeConnect = New stcShapeConnect
            With nodeBegin
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With
            Dim nodeEnd As stcShapeConnect = New stcShapeConnect
            With nodeEnd
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With

            With stcNewEdge
                .edgeBegin = nodeBegin
                .edgeEnd = nodeEnd
                .edgeDirection = stcConn
                .edgeShape = newPS
            End With

            Dim blnEdgeBuilt As Boolean = False

            For Each edge In dictEdge
                ' do the flags match
                If stcConn.ulngMask = edge.Value.edgeDirection.ulngMask Then
                    ' are the shapeids the same
                    If edge.Value.edgeShape.psShape = newPS.psShape Then
                        blnEdgeBuilt = True
                        Exit For
                    End If
                End If
            Next edge

            If blnEdgeBuilt = False Then
                dictEdge.Add(CLng(dictEdge.Count) + 1, stcNewEdge)
            End If


        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try


    End Sub


    '
    '
    '
    Private Sub processConnectionXelement(ByVal connXelem As XElement)

        Try

            ' initialize our variables
            Dim intConnection As Integer = 0
            Dim strFromSheet As String = "-1"
            Dim lngFromSheet As Long = -1
            Dim strFromCell As String = ""
            Dim strFromPart As String = "-1"
            Dim intFromPart As Integer = -1
            Dim strToSheet As String = "-1"
            Dim lngToSheet As Long = -1
            Dim strToCell As String = ""
            Dim strToPart As String = "-1"
            Dim intToPart As Integer = -1

            ' build our flag attribute structure
            Dim stcShape As stcFlagShapeAttr = New stcFlagShapeAttr
            ' a pageshape to point into the shapes dictionary 
            Dim stcNewNode As stcPageShape = New stcPageShape
            ' the new edge to work with
            Dim stcNewEdge As stcEdge = New stcEdge
            ' the connection attributes flag
            Dim stcConn As stcFlagEdgeAttr = New stcFlagEdgeAttr


            ' place holders for the vertices
            Dim nodeBegin As stcShapeConnect = New stcShapeConnect
            With nodeBegin
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With
            Dim nodeEnd As stcShapeConnect = New stcShapeConnect
            With nodeEnd
                .scPageId = Me.lngPgId
                .scShapeId = -1
                .scConnId = ""
                .scPart = -1
            End With


            ' get the list of attributes from the passed xelement and assign them
            '
            Dim listOfAttributes As IEnumerable(Of XAttribute) = _
                    From att In connXelem.Attributes() _
                    Select att

            For Each att As XAttribute In listOfAttributes
                ' Console.WriteLine("{0}={1}", att.Name.LocalName, att.Value)
                Select Case LCase(att.Name.LocalName)
                    ' from sheet is the dynamic connector
                    Case "fromsheet"
                        lngFromSheet = CLng(att.Value)
                    Case "fromcell"
                        strFromCell = CStr(att.Value)
                        ' above 100 is a control point
                    Case "frompart"
                        strFromPart = CStr(att.Value)
                        intFromPart = CShort(att.Value)
                        If intFromPart < FromPart.visControlPoint Then
                            strFromPart = CType(intFromPart, FromPart).ToString
                        Else
                            strFromPart = "visCtrlPt" & CStr(intFromPart - FromPart.visControlPoint + 1)
                        End If


                    Case "tosheet"
                        'strToSheet = CStr(att.Value)
                        lngToSheet = CLng(att.Value)
                    Case "tocell"
                        strToCell = CStr(att.Value)
                        ' above 100 is connection point
                    Case "topart"
                        intToPart = CShort(att.Value)
                        strToPart = CStr(att.Value)
                        If intToPart < toPart.visConnectionPoint Then
                            strToPart = CType(intToPart, toPart).ToString
                        Else
                            strToPart = "visConnPt" & CStr(intToPart - toPart.visConnectionPoint + 1)
                        End If
                End Select
            Next att

            ' pre-fill the node data
            With nodeBegin
                .scPageId = Me.lngPgId
                .scShapeId = lngFromSheet
                .scConnId = strFromCell
                .scPart = strFromPart
            End With
            With nodeEnd
                .scPageId = Me.lngPgId
                .scShapeId = lngToSheet
                .scConnId = strToCell
                .scPart = strToPart
            End With


            ' the shape object type is stored as a long
            stcShape.ulngMask = dictShapeTypes(lngFromSheet)
            ' check if from shape is a dynamic connector
            ' this is a generic test, rather than test for 1D and then look for special
            ' cases
            If stcShape.AllOn(stcFlagShapeAttr.Flag.typeConnector) Then

                stcConn.ulngMask = stcFlagEdgeAttr.Flag.edgeUndefined
                ' by definition a dynamic connector is simplex (directional shape), can be changed later
                stcConn.SetOn(stcFlagEdgeAttr.Flag.edgeSimplex)
                ' the edge will use an external shape
                stcConn.SetOn(stcFlagEdgeAttr.Flag.edgeComplex)
                '
                ' the dynamic connector edges were prebuilt, so go find it
                '
                For Each edge In dictEdge
                    ' do the flags match
                    If stcConn.ulngMask = edge.Value.edgeDirection.ulngMask Then
                        ' are the shapeids the same
                        If edge.Value.edgeShape.psShape = lngFromSheet Then
                            Dim lngEdgeKey As Long = edge.Key
                            ' copy edge to new edge
                            With stcNewEdge
                                .edgeBegin = edge.Value.edgeBegin
                                .edgeEnd = edge.Value.edgeEnd
                                .edgeDirection = edge.Value.edgeDirection
                                .edgeShape = edge.Value.edgeShape
                            End With

                            ' based on the passed connection update the new edge
                            If intFromPart = FromPart.visBegin Then
                                With nodeBegin
                                    .scPageId = Me.lngPgId
                                    .scShapeId = lngToSheet
                                    .scConnId = strToCell
                                    .scPart = strToPart
                                End With
                                stcNewEdge.edgeBegin = nodeBegin
                            ElseIf intFromPart = FromPart.visEnd Then
                                With nodeEnd
                                    .scPageId = Me.lngPgId
                                    .scShapeId = lngToSheet
                                    .scConnId = strToCell
                                    .scPart = strToPart
                                End With
                                stcNewEdge.edgeEnd = nodeEnd
                            End If
                            ' update the edge
                            dictEdge(lngEdgeKey) = stcNewEdge

                            Exit For
                        End If ' test for matching shapeid
                    End If
                Next edge


                ' this is not a dynamic connector - so special process

            Else

                '
                ' check to see if this is a group shape pretending to be 1D (rack diagrams)
                '
                If Me.bln1Dgroups = False Then
                    If (stcShape.AllOn(stcFlagShapeAttr.Flag.type1D And stcFlagShapeAttr.Flag.typeGroup) = True) _
                        And (strFromPart.Contains("visCtrlPt") = False) Then
                        Exit Sub
                    End If
                End If

                ' we are not dealing with a complex edge and no shape is supplied
                With stcNewNode
                    .psPage = Me.lngPgId
                    .psShape = -1
                End With

                ' make it a simplex/directional edge
                With stcConn
                    .ulngMask = stcFlagEdgeAttr.Flag.edgeUndefined
                End With
                stcConn.SetOn(stcFlagEdgeAttr.Flag.edgeSimplex)

                With stcNewEdge
                    .edgeBegin = nodeBegin
                    .edgeEnd = nodeEnd
                    .edgeShape = stcNewNode
                    .edgeDirection = stcConn
                End With

                Dim blnEdgeExists As Boolean = existsEdge(stcNewEdge)

                If blnEdgeExists = False Then
                    dictEdge.Add(CLng(dictEdge.Count) + 1, stcNewEdge)
                End If


            End If ' test for dynamic connector 


        Catch errXML As XmlException
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(errXML, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        Catch err As Exception
            Dim rethrow As Boolean = ExceptionPolicy.HandleException(err, "Log Only Policy")
            If (rethrow) Then
                Throw
            End If
        End Try
    End Sub




    '
    ' test for simplex (single direction) or complex (can go either direction but defined as external shape)
    '
    Private Function existsEdge(ByVal edgeTest As stcEdge) As Boolean

        Dim blnReturn As Boolean = False
        Dim edgeSwap As stcEdge

        Dim testEdge As stcFlagEdgeAttr = New stcFlagEdgeAttr
        ' clear the mask
        testEdge.ulngMask = edgeTest.edgeDirection.ulngMask

        ' if the edge is a simplex/directed path then we only need to verify the
        ' two nodes in the specified order
        If testEdge.AnyOn(stcFlagEdgeAttr.Flag.edgeSimplex) Then
            If dictEdge.ContainsValue(edgeTest) Then blnReturn = True
        End If

        ' test to see if an edge is available that goes in either direction
        If testEdge.AnyOn(stcFlagEdgeAttr.Flag.edgeComplex) Then
            edgeSwap = New stcEdge
            With edgeSwap
                .edgeDirection = edgeTest.edgeDirection
                .edgeBegin = edgeTest.edgeEnd
                .edgeEnd = edgeTest.edgeBegin
            End With

            If dictEdge.ContainsValue(edgeTest) Or dictEdge.ContainsValue(edgeSwap) Then blnReturn = True

        End If

        existsEdge = blnReturn

    End Function






#End Region


    '
    ' load it with the current working package, and the xdoc of the active page
    '
    Public Sub New(ByVal newPackage As clsVisPackage, _
                   ByVal newPgId As Long, _
                   ByVal newPage As XDocument, _
                   ByVal blnShow1Dgroups As Boolean)

        Me.m_visPackage = Nothing
        Me.visPackage = New clsVisPackage
        Me.visPackage = newPackage

        ' this should really be a collection since we can have more than one connector assigned to a connection point
        Me._dictEdge = Nothing
        Me.dictEdge = New Dictionary(Of Long, stcEdge)

        Me._xDoc = Nothing
        Me.xDoc = New XDocument
        Me.xDoc = newPage

        Me.lngPgId = newPgId

        Me.bln1Dgroups = blnShow1Dgroups

        If visMasters IsNot Nothing Then
            Me.m_visMasters = Nothing
            Me.m_visMasters = New clsMasters
        Else
            Me.m_visMasters = New clsMasters
        End If

        initMasterData()

        initShapeTypes()

        buildColOfConnectionPoints()

        clearUnconnectedShapes()

        processConnections()



    End Sub



    Protected Overrides Sub Finalize()

        If Me.m_visPackage IsNot Nothing Then
            Me.m_visPackage = Nothing
        End If

        If Me._xDoc IsNot Nothing Then
            Me._xDoc = Nothing
        End If

        If Me._dictEdge IsNot Nothing Then
            Me._dictEdge.Clear()
            Me._dictEdge = Nothing
        End If

        If Me._dictMaster IsNot Nothing Then
            Me._dictMaster.Clear()
            Me._dictMaster = Nothing
        End If



        MyBase.Finalize()
    End Sub



End Class


